#ifndef OMG_TDDS_SUB_SUBSCRIBER_HPP_
#define OMG_TDDS_SUB_SUBSCRIBER_HPP_

/* Copyright (c) 2009-2010, Real-Time Innovations, Inc.
 * Copyright (c) 2010, Object Management Group, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - Neither the names of the above copyright holders nor the names of their
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL A COPYRIGHT HOLDER OR CONTRIBUTOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */


namespace tdds { namespace sub {

template <typename DELEGATE>
class Subscriber : public tdds::core::Entity<DELEGATE> {
    OMG_DDS_REFERENCE_TYPE(Subscriber)

public:
    typedef std::vector<dds::sub::DataReaderBase> DataReaderSeqType;

public:
    typedef dds::core::StatusCondition<Subscriber> StatusConditionType;
public:

    /**
     * This operation retrieves a previously-created DataReader
     * belonging to the Subscriber that is attached to a Topic with a
     * matching topic_name. If no such DataReader exists, the operation
     * will return nil. If multiple DataReaders attached to the
     * Subscriber satisfy this condition, then the operation will return
     * one of them. It is not specified which one.  The use of this
     * operation on the built-in Subscriber allows access to the
     * built-in DataReader entities for the built-in topics
     */
    template <typename T>
    dds::sub::DataReader<T> lookup_datareader(const std::string& topic_name) const;

    template <typename T>
    dds::sub::DataReader<T>
    lookup_datareader(
        const dds::topic::TopicDescription<T>& topic_description) const;

    dds::type::builtin::StringDataReader
    lookup_datareader(
        const dds::topic::TopicDescription<std::string>& topic_description) const;
    
    dds::type::builtin::KeyedStringDataReader
    lookup_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedString>& topic_description) const;
    
    dds::type::builtin::BytesDataReader
    lookup_datareader(
        const dds::topic::TopicDescription<dds::core::ByteSeq>& topic_description) const;
    
    dds::type::builtin::KeyedBytesDataReader
    lookup_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedBytes>& topic_description) const;
    
    /**
     * This operation allows the application to access the DataReader
     * objects that contain samples with the specified sample_states,
     * view_states, and instance_states.  If the PRESENTATION QosPolicy
     * of the Subscriber to which the DataReader belongs has the
     * access_scope set to GROUP. This operation should only be
     * invoked inside a begin_access/end_access block. Otherwise it will
     * return the error PRECONDITION_NOT_MET.  Depending on the setting
     * of the PRESENTATION QoS policy (see Section 7.1.3.6,
     * PRESENTATION, on page 110), the returned collection of
     * DataReader objects may be a set containing each DataReader at
     * most once in no specified order, or a list containing each
     * DataReader one or more times in a specific order.
     * <ul>
     * <li>
     *   If PRESENTATION access_scope is INSTANCE or TOPIC, the returned
     *   collection is a set.
     * </li>
     *  <li>
     *    If PRESENTATION access_scope is GROUP and ordered_access is set
     *     to TRUE, then the returned collection is a list.
     *  </li>
     * </ul>
     * This difference is due to the fact that, in the
     * second situation it is required to access samples belonging to
     * different DataReader objects in a particular order. In this case,
     * the application should process each DataReader in the same order
     * it appears in the list and read or take exactly one sample from
     * each DataReader. The patterns that an application should use to
     * access data is fully described in Section 7.1.2.5.1, Access to
     * the data, on page 62.
     */
    const DataReaderSeqType
    data_readers(const dds::core::status::SampleState& sample_states =
                        dds::core::status::SampleState::any(),
                 const dds::core::status::ViewState& view_states =
                        dds::core::status::ViewState::any(),
                 const dds::core::status::InstanceState& instance_states =
                        dds::core::status::InstanceState::any()) const;

    DataReaderSeqType&
    data_readers(DataReaderSeqType& readers,
                 const dds::core::status::SampleState& sample_states =
                        dds::core::status::SampleState::any(),
                 const dds::core::status::ViewState& view_states =
                        dds::core::status::ViewState::any(),
                 const dds::core::status::InstanceState& instance_states =
                        dds::core::status::InstanceState::any()) const;

    /**
     * This operation invokes the operation on_data_available on the
     * DataReaderListener objects attached to contained DataReader
     * entities with a DATA_AVAILABLE status that is considered changed
     * as described in Section 7.1.4.2.2, Changes in Read Communication
     * Statuses.
     */
    void notify_datareaders();

    dds::domain::DomainParticipant parent() const;

    using dds::core::Entity::qos;
    const dds::sub::SubscriberQos qos() const;

    void qos(const dds::sub::SubscriberQos& qos);

    /**
     * This method closes the entity and releases all resources associated with
     * DDS, such as threads, sockets, buffers, etc. Any attempt to invoke
     * methods on a closed entity will raise an AlreadyClosedError.
     */
    void close();
    
public:
    template <typename T>
    dds::sub::DataReader<T> create_datareader(
        const dds::topic::TopicDescription<T>&    a_topic);
    template <typename T>
    dds::sub::DataReader<T> create_datareader(
        const dds::topic::TopicDescription<T>&    a_topic,
        const dds::sub::DataReaderQos&      a_qos,
        dds::sub::DataReaderListener<T>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());
    template <typename T>
    dds::sub::DataReader<T> create_datareader(
        const dds::topic::TopicDescription<T>&    a_topic,
        const std::string&                  qos_library_name,
        const std::string&                  qos_profile_name,
        dds::sub::DataReaderListener<T>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());

    dds::type::builtin::StringDataReader create_datareader(
        const dds::topic::TopicDescription<std::string>&    a_topic);
    dds::type::builtin::StringDataReader create_datareader(
        const dds::topic::TopicDescription<std::string>&    a_topic,
        const dds::sub::DataReaderQos&      a_qos,
        dds::sub::DataReaderListener<std::string>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());
    dds::type::builtin::StringDataReader create_datareader(
        const dds::topic::TopicDescription<std::string>&    a_topic,
        const std::string&                  qos_library_name,
        const std::string&                  qos_profile_name,
        dds::sub::DataReaderListener<std::string>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());

    dds::type::builtin::KeyedStringDataReader create_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedString>&    a_topic);
    dds::type::builtin::KeyedStringDataReader create_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedString>&    a_topic,
        const dds::sub::DataReaderQos&      a_qos,
        dds::sub::DataReaderListener<dds::type::builtin::KeyedString>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());
    dds::type::builtin::KeyedStringDataReader create_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedString>&    a_topic,
        const std::string&                  qos_library_name,
        const std::string&                  qos_profile_name,
        dds::sub::DataReaderListener<dds::type::builtin::KeyedString>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());

    dds::type::builtin::BytesDataReader create_datareader(
        const dds::topic::TopicDescription<dds::core::ByteSeq>&    a_topic);
    dds::type::builtin::BytesDataReader create_datareader(
        const dds::topic::TopicDescription<dds::core::ByteSeq>&    a_topic,
        const dds::sub::DataReaderQos&      a_qos,
        dds::sub::DataReaderListener<dds::core::ByteSeq>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());
    dds::type::builtin::BytesDataReader create_datareader(
        const dds::topic::TopicDescription<dds::core::ByteSeq>&    a_topic,
        const std::string&                  qos_library_name,
        const std::string&                  qos_profile_name,
        dds::sub::DataReaderListener<dds::core::ByteSeq>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());

    dds::type::builtin::KeyedBytesDataReader create_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedBytes>&    a_topic);
    dds::type::builtin::KeyedBytesDataReader create_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedBytes>&    a_topic,
        const dds::sub::DataReaderQos&      a_qos,
        dds::sub::DataReaderListener<dds::type::builtin::KeyedBytes>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());
    dds::type::builtin::KeyedBytesDataReader create_datareader(
        const dds::topic::TopicDescription<dds::type::builtin::KeyedBytes>&    a_topic,
        const std::string&                  qos_library_name,
        const std::string&                  qos_profile_name,
        dds::sub::DataReaderListener<dds::type::builtin::KeyedBytes>*    a_listener  = NULL,
        const dds::core::status::StatusMask&        a_mask      = dds::core::status::StatusMask::all());

    const dds::sub::DataReaderQos default_datareader_qos() const;
    void default_datareader_qos(const dds::sub::DataReaderQos &qos);
    void default_datareader_qos(const std::string& qos_library_name,
                                const std::string& qos_profile_name);

    // --- Entity methods: --- //
public:
    StatusConditionType status_condition();

    void listener(dds::sub::SubscriberListener* l,
                  const dds::core::status::StatusMask& mask = dds::core::status::StatusMask::all());

    dds::sub::SubscriberListener* listener() const;
};

}}
#endif /* OMG_TDDS_SUB_SUBSCRIBER_HPP_ */
